//
//  ViewController.swift
//  SwiftLua
//
//  Created by xiaerfei on 2019/7/19.
//  Copyright © 2019 xiaerfei. All rights reserved.
//
/*
 Swift 对象内存模型探究（一）
 https://zhuanlan.zhihu.com/p/26909719
 
 iOS 热更新解读（三）—— JSPatch 之于 Swift
 https://zltunes.github.io/2019/04/15/jspatch-swift/
 
 [iOS]原生swift的hotpatch可行性初探 续1
 https://yq.aliyun.com/articles/63173?spm=a2c4e.11153940.0.0.3a58375a8TJY8i
 */

import UIKit
import Lua
import UIColor_Hex_Swift
import yoga

public class aclass:NSObject {
    @objc dynamic public func hehe() {
        print("1111111")
    }
    
    @objc dynamic public func hehe1() {
        print("hehe1")
    }
}

public class BClass : aclass {
    public func mm() {
        
    }
}

public class CustomerView: UIView,CustomTypeInstance {
    public static func luaTypeName() -> String {
        return "customerview"
    }
}

class ViewController: UIViewController {
    @objc var a = "a"
    @objc private var pa = "pa"
    var array: [CustomerView] = []
    var globleClass: Dictionary<Int64, UIView> = [:]
    weak var cusview: CustomerView!
    let luaVM = Lua.VirtualMachine()
    
    override func viewDidLoad() {
        let view = LVView(frame: self.view.frame)
        view.runFile(name: "test", type: "lua")
        self.view.addSubview(view)
        // 注册控件
        JSContextShare.share.setObject(LVUIView.self, forKeyedSubscript: "View")
        JSContextShare.share.setObject(LVUILabel.self, forKeyedSubscript: "Label")
        // 注入脚本
        registerJS(src: "GlobalFunction", type: "js")
        registerJS(src: "app", type: "js")
        
        let crFunc = JSContextShare.share.context.objectForKeyedSubscript("createView")!
        let xmlString = readFile(src: "app", type: "xml")
        let string = parseXMLString(xml: xmlString!)
        
        let lvView = crFunc.call(withArguments: [string ?? ""])?.toObject()
        if lvView != nil {
            let v = lvView as! UIView
            v.frame = self.view.frame
//            v.ju_flxNode()?.layout()
            v.yoga.applyLayout(preservingOrigin: true)
            
            self.view.addSubview(v)
        }
        
        print(lvView as Any)
        
    }
    
    func parseXMLString(xml:String) -> String? {
        let rootXML = try? GDataXMLDocument(xmlString: xml)
        let rootElement = rootXML?.rootElement()
        if rootElement != nil {
            let array = parseElement(elements: [rootElement!], parent: nil)
            //首先判断能不能转换
            if (JSONSerialization.isValidJSONObject(array as Any)) {
                //利用OC的json库转换成OC的NSData，
                //如果设置options为NSJSONWritingOptions.PrettyPrinted，则打印格式更好阅读
                let data : Data! = try? JSONSerialization.data(withJSONObject: array as Any, options: [])
                //NSData转换成NSString打印输出
                let str = NSString(data:data, encoding: String.Encoding.utf8.rawValue)
                //输出json字符串
                return str as String?
            }
        }
        return nil
    }
    
    func parseElement(elements:Array<GDataXMLElement>, parent:GDataXMLElement? = nil) -> Array<Any>? {
        if elements.count == 0 { return nil }
        if elements.first!.isKind(of: GDataXMLElement.self) == false {
            return nil
        }
        var array:Array<Dictionary<String, Any>> = Array()
        
        for element in elements {
            var eleDict:Dictionary<String, Any> = Dictionary()
            eleDict["name"] = element.name()
            var attriDict:Dictionary<String, Any> = Dictionary()
            for n in element.attributes() {
                let node = n as! GDataXMLNode
                attriDict[node.name()] = node.stringValue()
            }
            eleDict["attri"] = attriDict
            if element.children() != nil && element.children()?.count != 0 {
                if (element.children()?.first as AnyObject).isKind(of: GDataXMLElement.self) {
                    let subs = parseElement(elements: element.children() as! Array<GDataXMLElement>, parent: element)
                    eleDict["subs"] = subs
                }
            }
            array.append(eleDict)
        }
        return array;
    }
    
    func registerJS(src:String, type: String) {
        let jsStr = readFile(src: src, type: type)
        if jsStr != nil {
            let _ = JSContextShare.share.evaluateScript(jsStr!)
        }
    }
    func readFile(src:String, type: String) -> String? {
        let file = Bundle.main.path(forResource: src, ofType: type)
        let url = URL(fileURLWithPath: file!)
        let xmlData = try! Data(contentsOf: url)
        let xmlString = String.init(data: xmlData, encoding: String.Encoding.utf8)
        return xmlString
    }
    
    public func objectVM() {
        let obj = self.luaVM.createTable()
        obj["new"] = self.luaVM.createFunction([String.arg], { args -> SwiftReturnValue in
            let (className) = (args.string)
            if className == "root" {
                let hash = Int64(self.view.hashValue)
                self.globleClass[hash] = self.view;
                return .value(hash)
            }
            let cls = NSClassFromString(className)! as! UIView.Type
            let v = cls.init()
            let hash = Int64(v.hashValue)
            self.globleClass[hash] = v;
            print(hash)
            return .value(hash)
        })
        
        obj["addSubView"] = self.luaVM.createFunction([Int64.arg,Int64.arg], { args -> SwiftReturnValue in
            let (selfId,subId) = (args.integer,args.integer)
            let selfView = self.globleClass[selfId]
            let subView = self.globleClass[subId]
            if subView != nil {
                selfView?.addSubview(subView!)
            }
            return .value(0)
        })
        
        obj["insertSuper"] = self.luaVM.createFunction([Int64.arg], { args -> SwiftReturnValue in
            let (viewId) = (args.integer)
            let subView = self.globleClass[viewId]
            if subView != nil {
                self.view .addSubview(subView!)
            }
            return .value(0)
        })
        
        obj["setValueForKey"] = self.luaVM.createFunction([Table.arg], { args -> SwiftReturnValue in
            let (table) = (args.table)
            let type = (table["type"] as! Number).toInteger()
            let name = table["name"] as! String
            let id = (table["id"] as! Number).toInteger()
            
            
            let view = self.globleClass[id]
            if type == 1 {
                let value = table["value"] as! Table
                let rect = value.toRect()
                if view != nil {
                    view!.setValue(rect, forKey: name)
                }
            } else if type == 2 {
                let colorName = table["value"]  as! String
                let color = UIColor(colorName)
                if view != nil {
                    view!.setValue(color, forKey: name)
                }
            }
            return .value(0)
        })
        
        
        self.luaVM.globals["viewobj"] = obj
        
        
        let path = Bundle.main.path(forResource: "test", ofType: "lua")
        do {
            let content = try String.init(contentsOfFile: path!, encoding: .utf8)
            let _ = self.luaVM.eval(content)
        } catch {
            print("Unexpected non-vending-machine-related error: \(error)")
        }
    }
    
    public func stringXVM() {
        let vm = Lua.VirtualMachine()
        let stringxLib = vm.createTable()
        
        stringxLib["split"] = vm.createFunction([String.arg, String.arg]) { args in
            let (subject, separator) = (args.string, args.string)
            let fragments = subject.components(separatedBy: separator)
            
            let results = vm.createTable()
            for (i, fragment) in fragments.enumerated() {
                results[i+1] = fragment
            }
            return .value(results)
        }
        
        stringxLib["UIViewNew"] = vm.createFunction([Number.arg, Number.arg, Number.arg, Number.arg]) { args in
            let (x, y, w,h) = (args.double,args.double,args.double,args.double)
            let view = CustomerView(frame: CGRect.init(x: x, y: y, width: w, height: h))
            let hash = Int64(view.hashValue)
            self.globleClass[hash] = view;
            print(hash)
            return .value(hash)
        }
        
        vm.globals["stringx"] = stringxLib
        
        let v = vm.eval("return stringx.split('hello world', ' ')")
        switch v {
        case let .values(vs):
            let array: [String] = (vs[0] as! Table).asSequence()
            print(array)
        case let .error(e):
            print(e)
        }
        
        switch vm.eval("return stringx.UIViewNew(100, 100, 100,50)") {
        case let .values(vs):
            let v = (vs[0] as! Number).toInteger()
            let view = self.globleClass[v]
            print(view!)
        case let .error(e):
            print(e)
        }
    }
    
    @objc dynamic public func fun() {
        print("ORIG fun self.a: \(self.a)")
    }
    
    @objc dynamic private func pfun() {
        print("ORIG pfun self.pa: \(self.pa)")
    }

}
